package com.bridgeintelligent.tag.bulkload.service.core;

import com.bridgeintelligent.tag.bulkload.api.BulkLoadException;
import com.bridgeintelligent.tag.bulkload.api.LookLikeExportDto;
import com.bridgeintelligent.tag.bulkload.api.fileload.FileLoadModel;
import com.bridgeintelligent.tag.bulkload.api.issue.CustomersChannel;
import com.bridgeintelligent.tag.bulkload.api.issue.IssueLog;
import com.bridgeintelligent.tag.bulkload.api.issue.IssueResult;
import com.bridgeintelligent.tag.bulkload.api.issue.IssueTaskLog;
import com.bridgeintelligent.tag.bulkload.service.core.issue.IssueIftService;
import com.bridgeintelligent.tag.bulkload.service.es.ESUtils;
import com.bridgeintelligent.tag.mapper.IssueTaskMapper;
import com.wayneleo.quickstart.framework.ExceptionCode;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.common.Strings;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.concurrent.atomic.AtomicInteger;


@Slf4j
@AllArgsConstructor
@Service
public class IssueLookLikeHandler {

    private static final String ALL_EXPORT = "0";

    private static final String SCORE = "score";

    private static final String CUSTNO = "cust_id";

    private static final String FIRST_CODE = "cust_id|score|";

    private static final String FIRST_NAME = "客户号|评分值|";

    private static final String DIVIDE_SIGN = "|";

    private static final String LOCAL_FILE_FORMAT = "/data/issue/%s_%s_%s";

    private static final String LOCAL_FILE_FORMAT_DATA = "%s_data.txt";

    private static final String LOCAL_FILE_FORMAT_DICT = "%s_dict.txt";

    private static final String LOCAL_FILE_FORMAT_CHECK = "%s_check.txt";

    private final IssueTaskMapper issueTaskMapper;

    private IssueIftService issueIftService;


    public IssueResult handler(IssueTaskLog issueTaskLog, IssueLog issueLog, LookLikeExportDto lookLikeConfig) throws ParseException {
        FileLoadModel fileLoadModel = createFileLoad(lookLikeConfig);
        CustomersChannel customersChannel = issueTaskMapper.findChannel(issueTaskLog.getChannelId());
        String dataDate = issueLog.getDataDate();
        String tempFilePre = issueIftService.getTempPath(issueTaskLog,dataDate, customersChannel,LOCAL_FILE_FORMAT);
        File dataTempFile = new File(String.format(LOCAL_FILE_FORMAT_DATA,tempFilePre));
        if (!dataTempFile.exists()) {
            dataTempFile.getParentFile().mkdir();
        }
        try (FileOutputStream fos = new FileOutputStream(dataTempFile); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(fos))) {
            AtomicInteger num = new AtomicInteger();
            writeHandler(lookLikeConfig.getEsIndex(), fileLoadModel, num, bufferedWriter);
            File okFile = createOkFile(num,tempFilePre);
            File dictFile = createDictFile(tempFilePre);
            bufferedWriter.flush();
            checkFile(dataTempFile,num);
            return new IssueResult().setNum(num.get()).setFilePath(issueIftService.upload(dataDate,
                    issueTaskLog,issueLog,customersChannel,dataTempFile,dictFile,okFile));
        } catch (IOException e) {
            log.error("写入本地文件出现异常", e);
            throw new BulkLoadException(new ExceptionCode(999, "写入本地文件出现异常"));
        }catch (NullPointerException e) {
            log.error("查询结果为空", e);
            throw new BulkLoadException(new ExceptionCode(999, "查询结果为空"));
        } catch (Exception e) {
            log.error("线程休眠异常",e);
            throw new BulkLoadException(new ExceptionCode(999, "生成下发文件出现中断异常"));
        }
    }

    private File createDictFile(String pre) throws IOException {
        File dataTempFile = new File(String.format(LOCAL_FILE_FORMAT_DICT,pre));
        if (!dataTempFile.exists()) {
            dataTempFile.getParentFile().mkdir();
        }
        try (FileOutputStream fos = new FileOutputStream(dataTempFile); BufferedWriter bufferedWriter =
                new BufferedWriter(new OutputStreamWriter(fos))) {
            bufferedWriter.flush();
            return dataTempFile;
        }
    }

    private File createOkFile(AtomicInteger num, String pre) throws IOException {
        File dataTempFile = new File(String.format(LOCAL_FILE_FORMAT_CHECK,pre));
        if (!dataTempFile.exists()) {
            dataTempFile.getParentFile().mkdir();
        }
        try (FileOutputStream fos = new FileOutputStream(dataTempFile); BufferedWriter bufferedWriter =
                new BufferedWriter(new OutputStreamWriter(fos))) {
            bufferedWriter.write(FIRST_CODE);
            bufferedWriter.newLine();
            bufferedWriter.write(FIRST_NAME);
            bufferedWriter.newLine();
            bufferedWriter.write(String.valueOf(num));
            bufferedWriter.newLine();
            bufferedWriter.flush();
            return dataTempFile;
        }catch (Exception e){
            throw e;
        }
    }

    private void checkFile(File file, AtomicInteger num) throws IOException {
        long count = Files.lines(Paths.get(file.getPath())).count();
        if(count == num.intValue()){
            return;
        }
        log.info("下发文件校验{}-{}",num,count);
        throw new BulkLoadException(new ExceptionCode(999, "生成的文件校验失败"));
    }

    private void writeHandler(String indexName, FileLoadModel fileLoadModel, AtomicInteger num, BufferedWriter bufferedWriter) {
        ESUtils.scrollSearch(indexName, fileLoadModel, hits -> {
            write(fileLoadModel, num, bufferedWriter, hits);
        });
    }

    private void write(FileLoadModel fileLoadModel, AtomicInteger num, BufferedWriter bufferedWriter, SearchHit[] hits) throws IOException {
        for (SearchHit hit : hits) {
            StringBuilder tmp = new StringBuilder();
            num.getAndIncrement();
            for(String feild :fileLoadModel.getIncludes()){
                Object source = hit.getSourceAsMap().get(feild);
                if(source == null){
                    tmp.append("").append(DIVIDE_SIGN);
                }else {
                    String queryResult = source.toString();
                    tmp.append(queryResult).append(DIVIDE_SIGN);
                }
            }
            bufferedWriter.write(tmp.toString());
            bufferedWriter.newLine();
        }
    }

    private FileLoadModel createFileLoad(LookLikeExportDto lookLikeConfig) {
        FileLoadModel fileLoadModel = new FileLoadModel();
        //如果是全部数据导出
        QueryBuilder queryBuilder;
        if (lookLikeConfig.getExportType().equals(ALL_EXPORT)){
            queryBuilder = QueryBuilders.matchAllQuery();
        }else{
            String[] minMax = lookLikeConfig.getMinMax().split("-");
            if (lookLikeConfig.getFlag().equals("∞")){
                queryBuilder = QueryBuilders.rangeQuery(SCORE).gte(Double.parseDouble(minMax[0]));
            }else{
                queryBuilder = QueryBuilders.boolQuery()
                        .must(QueryBuilders.rangeQuery(SCORE).gte(Double.parseDouble(minMax[0])))
                        .must(QueryBuilders.rangeQuery(SCORE).lt(Double.parseDouble(minMax[1])));
            }
        }
        fileLoadModel.setQueryBuilder(queryBuilder);
        fileLoadModel.setExcludes(Strings.EMPTY_ARRAY);
        fileLoadModel.setIncludes(new String[]{CUSTNO,SCORE});
        return fileLoadModel;
    }

}
